/**
 * 
 */
package bot;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

import account.Account;
import botactions.EndOfMOTDAction;
import botactions.ErrorAction;
import botactions.IRCServerTypeAction;
import botactions.JoinAction;
import botactions.KickAction;
import botactions.NickAction;
import botactions.NickNameAlreadyInUseAction;
import botactions.PingAction;
import botactions.PrivmsgAction;

import specialtriggers.FavArtistTrigger;
import specialtriggers.FavTrackTrigger;
import specialtriggers.HyllaDinNationTrigger;
import specialtriggers.HyllaDinSannaNationTrigger;
import specialtriggers.HyllaWombosNationTrigger;
import specialtriggers.NewAccountTrigger;
import specialtriggers.NewTriggerTrigger;
import specialtriggers.NowPlayingTrigger;
import specialtriggers.ReloadAccountsTrigger;
import specialtriggers.ReloadTriggersTrigger;
import specialtriggers.UptimeTrigger;
import specialtriggers.VersionTrigger;
import trigger.ExactStringFlag;
import trigger.IgnoreCaseFlag;
import trigger.PrivmsgResponseFlag;
import trigger.Trigger;
import trigger.TriggerFactory;

/**
 * @author Philip
 *
 */
public class IRCBot {
	public static final String VERSION = "Cjuubot v2.0.0 beta";
	
	public static final String CHANNEL_MODE = "324";
	public static final String END_OF_MOTD = "376";
	public static final String NAMES_LIST = "353";
	public static final String NICKNAME_ALREADY_IN_USE = "433";
	
	private String nickName;
	private String fullName;
	private String hostName;
	private String authName;
	
	private boolean rejoinOnKick;
	
	private long startTime;
	
	private IRCServerConnection connection;
	private ArrayList<Trigger> triggerList;
	private HashMap<String, Account> accountList;
	private HashSet<String> channelList;
		
	private HashMap<String, IRCServerTypeAction> typeActions = new HashMap<String, IRCServerTypeAction>();
	
	public IRCBot() {
		
		rejoinOnKick = true;
		
		startTime = System.currentTimeMillis();
		
		channelList = new HashSet<String>();
		triggerList = new ArrayList<Trigger>();
		accountList = new HashMap<String, Account>();
		
		addTrigger(new PrivmsgResponseFlag(new IgnoreCaseFlag(new ExactStringFlag(new UptimeTrigger("!NICK uptime", this)))));
		addTrigger(new PrivmsgResponseFlag(new IgnoreCaseFlag(new ExactStringFlag(new HyllaDinNationTrigger("!NICK hylla din nation")))));
		addTrigger(new PrivmsgResponseFlag(new IgnoreCaseFlag(new ExactStringFlag(new HyllaDinSannaNationTrigger("!NICK hylla din sanna nation")))));
		addTrigger(new PrivmsgResponseFlag(new IgnoreCaseFlag(new ExactStringFlag(new HyllaWombosNationTrigger("!NICK hylla wombos nation")))));
		addTrigger(new PrivmsgResponseFlag(new IgnoreCaseFlag(new ExactStringFlag(new NewTriggerTrigger("!NICK add: !VARIABLE", "Gjort! :>", this, new TriggerFactory())))));
		addTrigger(new PrivmsgResponseFlag(new IgnoreCaseFlag(new ExactStringFlag(new ReloadTriggersTrigger("!NICK reload triggers", "Gjort! :>", this)))));
		addTrigger(new PrivmsgResponseFlag(new IgnoreCaseFlag(new ExactStringFlag(new ReloadAccountsTrigger("!NICK reload accounts", "Gjort! :>", this)))));
		addTrigger(new PrivmsgResponseFlag(new IgnoreCaseFlag(new ExactStringFlag(new NewAccountTrigger("!NICK register: !VARIABLE", "Gjort! :>", this)))));
		addTrigger(new PrivmsgResponseFlag(new IgnoreCaseFlag(new ExactStringFlag(new VersionTrigger("!NICK version")))));
		addTrigger(new PrivmsgResponseFlag(new IgnoreCaseFlag(new ExactStringFlag(new NowPlayingTrigger("!np !VARIABLE")))));
		addTrigger(new PrivmsgResponseFlag(new IgnoreCaseFlag(new ExactStringFlag(new FavArtistTrigger("!favartist !VARIABLE")))));
		addTrigger(new PrivmsgResponseFlag(new IgnoreCaseFlag(new ExactStringFlag(new FavTrackTrigger("!favtrack !VARIABLE")))));
		
		typeActions.put("PING", new PingAction());
		typeActions.put("ERROR", new ErrorAction());
		typeActions.put("PRIVMSG", new PrivmsgAction());
		typeActions.put("JOIN", new JoinAction());
		typeActions.put("KICK", new KickAction());
		typeActions.put("NICK", new NickAction());
		typeActions.put(END_OF_MOTD, new EndOfMOTDAction());
		typeActions.put(NICKNAME_ALREADY_IN_USE, new NickNameAlreadyInUseAction());
		
	}
	
	public void connect() {
		connection.connect();
		
		if (connection.isConnected()) {
			write("NICK " + nickName);
			write("USER " + nickName + " ignored " + connection.getServer() + " :" + fullName);
		}
		
		while (connection.isConnected()) {
			readLine();
		}
		
		System.out.println("DISCONNECTED");
		
		try {
			System.out.println("Reconnecting in 10 seconds...");
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		connect();
	}
	
	public void readLine() {
		String line = connection.readLine();
		System.out.println(line);

		if (line == null) {
			connection.close();
			return;
		}
		
		
		String type = null;
		
		if (!line.startsWith(":")) {
			type = line.substring(0, line.indexOf(" "));
		} else {
			String typeStart = line.substring(line.indexOf(" ") + 1);
			type = typeStart.substring(0, typeStart.indexOf(" "));
		}
				
		IRCServerTypeAction typeAction = typeActions.get(type);
				
		if (typeAction != null) {
			typeAction.execute(this, line);
		}
	}
	
	public void write(String line) {
		connection.write(line);
		System.out.println(line);
	}
	
	public void addTrigger(Trigger trigger) {
		triggerList.add(trigger);
	}
	
	public void removeTrigger(String trigger) {
		
	}
	
	public void removeAllTriggers() {
		triggerList = new ArrayList<Trigger>();
	}
	
	public void addAccount(String senderHostname, Account account) {
		accountList.put(senderHostname, account);
	}
	
	public void changeAccountRank(String senderHostname, int rank) {
		Account user = accountList.get(senderHostname);
		user.setRank(rank);
	}
	
	public void removeAllAccounts() {
		accountList = new HashMap<String, Account>();
	}
	
	public void respondToTrigger(String sender, String receiver, String senderHostname, String input) {
		Account user = accountList.get(senderHostname);
		
		if (user == null) {
			user = Account.STANDARD_ACCOUNT;
		}
		
		for (Trigger t : triggerList) {
			t.updateVariables(nickName, sender, receiver, senderHostname, input);
			if ((user.rank() <= t.rank()) && t.triggers(input)) {
				write(t.response());
				break;
			}
		}
	}
	
	public void addChannel(String channel) {
		channelList.add(channel);
	}
	public String channels() {
		StringBuilder sb = new StringBuilder();
		for (String s : channelList) {
			sb.append(s + ",");
		}
		return sb.toString().substring(0, sb.toString().length() - 1);
	}
	
	public void removeChannel(String channel) {
		channelList.remove(channel);
	}
	
	public boolean rejoinsOnKick() {
		return rejoinOnKick;
	}
	
	public String uptime() {
		long uptime = (System.currentTimeMillis() - startTime) / 1000;

        int d = (int)(uptime / 86400);
        uptime %= 86400;
        int h = (int)(uptime / 3600);
        uptime %= 3600;
        int m = (int)(uptime / 60);
        uptime %= 60;
        int s = (int)uptime;
        
        return "Uptime: " + d + " days " + h + " hours " + m + " minutes " + s + " seconds";
	}
	
	public void createConnection(String server, int port) {
		connection = new IRCServerConnection(server, port);
	}
	
	public void load(FileLoader fileLoader) {
		fileLoader.load(this);
	}
	
	
	public void setNickName(String nickName) {
		this.nickName = nickName;
	}
	
	public String getNickName() {
		return nickName;
	}
	
	public void setFullName(String fullName) {
		this.fullName = fullName;
	}

	public void setAuthName(String authName) {
		this.authName = authName;
	}
	
	public static void main(String[] args) {
		SettingsLoader settingsLoader = new SettingsLoader("settings.ini");
		TriggerLoader triggerLoader = new TriggerLoader("triggers.txt");
		AccountLoader accountLoader = new AccountLoader("accounts.txt");
		
		IRCBot cjuubot = new IRCBot();
		cjuubot.load(settingsLoader);
		cjuubot.load(triggerLoader);
		cjuubot.load(accountLoader);

		cjuubot.connect();
	}

}
